package com.lonefennec.chameleon;

import java.util.LinkedList;
import java.util.List;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.lonefennec.chameleon.characters.Guard;
import com.lonefennec.chameleon.characters.Player;
import com.lonefennec.chameleon.ui.AlertLevel;
import com.lonefennec.chameleon.ui.JumpButton;
import com.lonefennec.chameleon.ui.MoveLeftButton;
import com.lonefennec.chameleon.ui.MoveRightButton;

public class ChameleonGame extends InputAdapter implements ApplicationListener
{
	//class to store the states of the touchscreen buttons
	public static final class ButtonInput
	{		
		private boolean left = false;
		private boolean right = false;
		private boolean jump = false;
		private boolean action = false;
		
		public boolean isLeft()
		{
			return left;
		}
		
		public boolean isRight()
		{
			return right;
		}
		
		public boolean isJump()
		{
			return jump;
		}
		
		public boolean isAction()
		{
			return action;
		}
		
		public void setLeft(boolean state)
		{
			left = state;
		}
		
		public void setRight(boolean state)
		{
			right = state;
		}
		
		public void setJump(boolean state)
		{
			jump = state;
		}
		
		public void setAction(boolean state)
		{
			action = state;
		}
	}
	
	public static final ButtonInput buttonInput = new ButtonInput();
	
	public static final class GameState
	{
		public float alertLevel = 0;
	}
	
	public static GameState state; 
	
	private SpriteBatch batch;
	
	private Stage stage;
	private OrthographicCamera camera;
	
	private Stage uiStage;
	private OrthographicCamera uiCamera;
	
	public static Player player;
	public static final Level level = new Level();
	public static final List<Guard> guards = new LinkedList<Guard>();
	
	public static AssetManager assets;
	
	@Override
	public void create()
	{
		try
		{
			Gdx.input.setInputProcessor(this);
			
			assets = new AssetManager();
			
			float w = Gdx.graphics.getWidth();
			float h = Gdx.graphics.getHeight();
			
			camera = new OrthographicCamera(1, h / w);
			stage = new Stage(w, h, true);
			stage.setCamera(camera);
			
			uiCamera = new OrthographicCamera(w, h);
			uiStage = new Stage(w, h, true);
			uiStage.setCamera(uiCamera);
			
			batch = new SpriteBatch();	
			
			state = new GameState();
			
			Physics.initialize();
			
			level.loadLevel("levels/test.json");
			
			TilingBackground background = new TilingBackground(assets, "tiles/metal.jpg");
			background.speedFactor = 0.5f;
			
			stage.addActor(background);
			stage.addActor(level);
			
			Group tongueGroup = new Group();
			stage.addActor(tongueGroup);
			
			player = new Player(tongueGroup, uiStage);
			level.addActor(player);			
			
			//spawn at the level's first spawn point
			Vector2 spawnPos = level.getPlayerStart(0);
			player.setBodyPos(spawnPos);
			
			int guardCount = level.getGuardPatrolCount();
			for (int guardIt = 0; guardIt < guardCount; ++guardIt)
			{
				Rectangle patrol = level.getGuardPatrol(guardIt);
				
				Vector2 guardPos = new Vector2(patrol.x, patrol.y);
				
				Guard guard = new Guard(patrol);
				guard.setBodyPos(guardPos);

				guards.add(guard);
				stage.addActor(guard);
			}
			
			float bottomBtnSize = h * 0.20f;
			
			uiStage.addActor(new MoveLeftButton("ui/right-arrow.png", new Vector2(0, 0), bottomBtnSize));
			uiStage.addActor(new MoveRightButton("ui/right-arrow.png", new Vector2(bottomBtnSize, 0), bottomBtnSize));
			
			uiStage.addActor(new JumpButton("ui/up-arrow.png", new Vector2(w - bottomBtnSize, 0), bottomBtnSize));
			
			uiStage.addActor(new AlertLevel());
			
		} catch (Exception e)
		{
			System.err.print("Error initializing game: " + e.getMessage());
			
			Gdx.app.exit();
		}
	}
	
	@Override
	public void dispose()
	{
		stage.dispose();
		uiStage.dispose();
		
		Physics.dispose();
		
		batch.dispose();
	}
	
	private void updateAlertLevel(float delta)
	{
		float totalAlarm = 0;
		
		final float ALARM_MULTIPLER = 5; //todo: difficulty
		
		for (Guard guard : guards)
		{
			if (guard.isAlarmed())
			{
				float distance2 = new Vector2(guard.x, guard.y).dst2(new Vector2(player.x, player.y));
				float vision2 = (float) Math.pow(guard.getVisionRange(), 2);
				
				float alarm = 1.0f - distance2 / vision2;
				alarm = Math.max(0.1f, alarm);
				alarm = Math.min(1.0f, alarm);
				
				totalAlarm += ALARM_MULTIPLER * alarm;
			}
		}
		
		float change;
		if (totalAlarm > 0)
			change = totalAlarm;//1.0f * alarmedGuards;
		else
			change = -0.2f;
		
		state.alertLevel += change * delta;
		state.alertLevel = Math.min(state.alertLevel, 1);
		state.alertLevel = Math.max(state.alertLevel, 0);
	}
	
	@Override
	public void render()
	{
		float delta = Gdx.graphics.getDeltaTime();
		
		stage.act(delta);//1.0f / 20);
		uiStage.act(delta);
		
		updateAlertLevel(delta);		
		
		Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		float screenWidth = Gdx.graphics.getWidth();
		float screenHeight = Gdx.graphics.getHeight();
		float worldScreenWidth = Physics.SCREEN_TO_WORLD * screenWidth;
		float worldScreenHeight = Physics.SCREEN_TO_WORLD * screenHeight;
		
		camera.setToOrtho(false, worldScreenWidth, worldScreenHeight);
		camera.position.set(player.x, player.y, 0);
		camera.update();
		
		uiCamera.setToOrtho(false, screenWidth, screenHeight);
		//uiCamera.position.set(-screenWidth/2, -screenHeight/2, 0);
		//uiCamera.position.set(player.x, player.y, 0);
		uiCamera.update();
		
		stage.draw();
		uiStage.draw();
		
		//debug drawing of physics bodies
		batch.begin();
		//Physics.draw(camera.combined);
		batch.end();
		
		Physics.simulate(Gdx.graphics.getDeltaTime());
	}
	
	@Override
	public boolean touchDown(int x, int y, int pointerId, int button)
	{
		return uiStage.touchDown(x, y, pointerId, button);
	}
	
	@Override
	public boolean touchUp(int x, int y, int pointerId, int button)
	{
		return uiStage.touchUp(x, y, pointerId, button);
	}
	
	@Override
	public void resize(int width, int height)
	{
	}
	
	@Override
	public void pause()
	{
	}
	
	@Override
	public void resume()
	{
	}
}
